use super::{hashmap, HashMap, TokenType, AST, GLOBAL, INDEX_OUT_OF_BOUNDS};

#[derive(Debug, Default, Clone)]
pub struct SemanticAnalyzer {
    root: Box<AST>,
}

impl SemanticAnalyzer {
    pub fn new(root: &AST) -> Self {
        Self {
            root: Box::new(root.clone()),
        }
    }
    pub fn symbol_table(&self) -> HashMap<String, HashMap<String, (i32, i32)>> {
        let global_str = String::from(GLOBAL);
        let root = self.root.as_ref();
        let mut symbol_table = hashmap!(global_str.clone() => HashMap::default());
        let mut global_idx = 0;
        for decl_node_ptr in &*root.children {
            /*
                __VarDecl | __FuncDecl
            */
            if decl_node_ptr.token_type == TokenType::FuncDecl {
                /*
                    TOKEN_TYPE::FUNC_DECL
                        |
                        |---- __Type
                        |
                        |---- TOKEN_TYPE::ID
                        |
                        |---- __Params
                        |
                        |---- __CompoundStmt
                */
                let mut var_idx = 0;
                let token_1 = decl_node_ptr.children.get(1).expect(INDEX_OUT_OF_BOUNDS);
                let func_name = &*token_1.token_str;

                symbol_table.insert(func_name.clone(), Default::default());

                let token_2_opt = decl_node_ptr.children.get(2);
                if token_2_opt.is_some(){
                    let token_2 = token_2_opt.unwrap();
                    for param_ptr in &*token_2.children {
                        let var_name = &*param_ptr.children.get(1).expect(INDEX_OUT_OF_BOUNDS).token_str;
                        symbol_table
                            .entry(func_name.clone())
                            .or_insert(HashMap::default())
                            .insert(String::from(var_name), (var_idx, 0));
                        var_idx += 1;
                    }
                }
                for var_decl_ptr in &*decl_node_ptr
                    .children
                    .get(3)
                    .expect(INDEX_OUT_OF_BOUNDS)
                    .children
                    .get(0)
                    .expect(INDEX_OUT_OF_BOUNDS)
                    .children
                {
                    let var_name = &*var_decl_ptr
                        .children
                        .get(1)
                        .expect(INDEX_OUT_OF_BOUNDS)
                        .token_str;
                    let var_size = if var_decl_ptr.children.len() == 2 {
                        0
                    } else {
                        let temp = &*var_decl_ptr
                            .children
                            .get(2)
                            .expect(INDEX_OUT_OF_BOUNDS)
                            .token_str;
                        temp.parse().unwrap()
                    };
                    symbol_table
                        .entry(func_name.clone())
                        .or_insert(HashMap::default())
                        .insert(String::from(var_name), (var_idx, var_size));
                    var_idx += var_size + 1;
                }
            } else {
                let var_name = &*decl_node_ptr
                    .children
                    .get(1)
                    .expect(INDEX_OUT_OF_BOUNDS)
                    .token_str;
                let var_size = if decl_node_ptr.children.len() == 2 {
                    0
                } else {
                    let temp = &*decl_node_ptr
                        .children
                        .get(2)
                        .expect(INDEX_OUT_OF_BOUNDS)
                        .token_str;
                    temp.parse().unwrap_or_else(|x| {
                        panic!("temp:{}, child2:{:?}", temp, decl_node_ptr.children.get(2))
                    })
                };
                symbol_table
                    .entry(global_str.clone())
                    .or_insert(HashMap::default())
                    .insert(String::from(var_name), (global_idx, var_size));
                global_idx += var_size + 1;
            }
        }
        symbol_table
    }
}
